package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.constant.UserCacheConstant;
import com.zzyl.dto.LoginDTO;
import com.zzyl.exception.BaseException;
import com.zzyl.properties.JwtTokenManagerProperties;
import com.zzyl.service.LoginService;
import com.zzyl.service.ResourceService;
import com.zzyl.service.RoleDeptService;
import com.zzyl.service.RoleService;
import com.zzyl.utils.JwtUtil;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.ResourceVo;
import com.zzyl.vo.RoleVo;
import com.zzyl.vo.UserAuth;
import com.zzyl.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *  登录接口实现
 */
@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RoleService roleService;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private JwtTokenManagerProperties jwtTokenManagerProperties;

    @Autowired
    private RoleDeptService roleDeptService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 后台登录
     * @param loginDTO 登录信息
     * @return
     */
    @Override
    public UserVo login(LoginDTO loginDTO) {
        // 1.认证用户 获取认证对象
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword());
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        // 2.判断是否认证成功
        if (!authentication.isAuthenticated()) {
            // 认证失败抛出业务异常提示
            throw new BaseException("权限认证失败");
        }
        // 3.获取认证用户信息
        UserAuth userAuth = BeanUtil.toBean(authentication.getPrincipal(), UserAuth.class);
        UserVo userVo = BeanUtil.copyProperties(userAuth, UserVo.class);
        userVo.setPassword("");
        // 4.获取当前用户资源列表
        List<ResourceVo> resourceVoList = resourceService.findResourceVoListByUserId(userVo.getId());
        if (CollUtil.isNotEmpty(resourceVoList)) {
            Set<String> UserResourcePathSet = resourceVoList.stream()
                    .filter(resourceVo -> ObjectUtil.equal(resourceVo.getResourceType(), "r"))
                    .map(ResourceVo::getRequestPath)
                    .collect(Collectors.toSet());
            userVo.setResourceRequestPaths(UserResourcePathSet);
        }
        // 5.获取当前用户角色列表
        List<RoleVo> roleVoList = roleService.findRoleVoListByUserId(userVo.getId());
        if (CollUtil.isNotEmpty(roleVoList)) {
            Set<String> userRoleLabelSet = roleVoList.stream().map(RoleVo::getLabel).collect(Collectors.toSet());
            userVo.setRoleLabels(userRoleLabelSet);
        }
        // 6.生成uuidToken
        String uuidToken = IdUtil.simpleUUID();
        userVo.setUserToken(uuidToken);
        // 6.1 生成JwtToken
        Map<String, Object> claims = new HashMap<>();
        claims.put("currentUser", JSONUtil.toJsonStr(userVo));
        String jwtToken = JwtUtil.createJWT(
                jwtTokenManagerProperties.getBase64EncodedSecretKey(),
                jwtTokenManagerProperties.getTtl(),
                claims
        );
        // redis过期时间
        long ttl = jwtTokenManagerProperties.getTtl() / 1000;
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        // 7.存储uuidToken到redis   username:uuid
        String userTokenKey = UserCacheConstant.USER_TOKEN + userVo.getUsername();
        opsForValue.set(userTokenKey, uuidToken, ttl, TimeUnit.SECONDS);
        // 7.1 存储jwtToken到redis  uuid:jwToken    方便后登录的用户替换旧登录的用户
        String jwtTokenKey = UserCacheConstant.JWT_TOKEN + uuidToken;
        opsForValue.set(jwtTokenKey, jwtToken, ttl, TimeUnit.SECONDS);
        // 8.返回结果
        return userVo;
    }
}
